use std::sync::Arc;
use db::Db;
use tokio::sync::Mutex;

use anyhow::Result;
// use db::{s};
// use dotenv::dotenv;
use helpers::Bot;
use teloxide::{dispatching::UpdateFilterExt, prelude::*, types::Update};

mod handlers;
mod helpers;
mod keyboards;

#[tokio::main(flavor = "multi_thread", worker_threads = 10)]
async fn main() {
    // Get values from .env file
    // dotenv().ok();
    run_bot().await;
}

use teloxide::utils::command::BotCommands;
#[derive(BotCommands, Debug)]
#[command(rename_rule = "lowercase")]
enum Command {
    Start,
    Random,
    CountUnread,
    CountUnreadTime,
    GetAllPosts,
}
/// Parse what a command it is
async fn command_answer(bot: Bot, m: Message, command: Command, db: Db) -> Result<()> {
    match command {
        Command::Start => {
            handlers::start(bot, m, db).await?;
            Ok(())
        }
        Command::Random => {
            handlers::random(bot, m, db).await?;
            Ok(())
        }
        Command::CountUnread => {
            handlers::count_unread_posts(bot, m, db).await?;
            Ok(())
        }
        Command::CountUnreadTime => {
             handlers::count_unread_time(bot, m, db).await?;
            Ok(())
        }
        Command::GetAllPosts => {
            // handlers::get_all_posts(bot, m, db).await?;
            Ok(())
        }
    }
}
/// If message is a command - parse like a command. Else - it is a url to save
pub async fn messages_handler(m: Message, bot: Bot, db: Db) -> Result<()> {
    println!("messages_handler");
    if let Some(text) = parse_text(&m) {
        if let Ok(command) = BotCommands::parse(&text, "readlaterbot") {
            command_answer(bot, m, command, db).await?;
        } else {
            handlers::add(bot, m, db).await?;
        }
    }
    Ok(())
}
fn parse_text(m: &Message) -> Option<String> {
    let mut res = None;
    if let Some(text) = m.text() {
        res = Some(text.to_string());
    }
    if let Some(text) = m.caption() {
        res = Some(text.to_string());
    }
    res
}

async fn handle_callback_query(q: CallbackQuery, bot: Bot, db: Db) -> Result<()> {
    let data = q.data.clone();
    match data {
        None => {}
        Some(data) => {
            // TODO: ref using enums
            if data.starts_with("del") {
                crate::handlers::delete(bot, q, data.strip_prefix("del ").unwrap(), db).await?;
            } else if data.starts_with("archive") {
                crate::handlers::archive(bot, q, data.strip_prefix("archive ").unwrap(), db).await?;
            } else if data.starts_with("unarchive") {
                crate::handlers::unarchive(bot, q, data.strip_prefix("unarchive ").unwrap(), db)
                    .await?;
            } else if data.starts_with("page") {
                // crate::handlers::change_page(bot, q, data.strip_prefix("page ").unwrap(), db).await?;
            } else if data.starts_with("post") {
                // crate::handlers::get_post_info(bot, q, data.strip_prefix("post ").unwrap(), db).await?;
            } else if data.starts_with("favorite") {
                crate::handlers::favorite(bot, q, data.strip_prefix("favorite ").unwrap(), db).await?;
            } else if data.starts_with("unfavorite") {
                crate::handlers::unfavorite(bot, q, data.strip_prefix("unfavorite ").unwrap(), db)
                    .await?;
            }
        }
    }
    Ok(())
}
async fn run_bot() {
    pretty_env_logger::formatted_builder()
        .filter(
            Some(&env!("CARGO_CRATE_NAME").replace('-', "_")),
            log::LevelFilter::Info,
        )
        .filter(Some("teloxide"), log::LevelFilter::Info)
        //.target(pretty_env_logger::env_logger::Target::Stdout)
        .init();
    let url = std::env::var("POSTGRES_URL").unwrap();
    let db = db::Db::connect(&url).await.unwrap();
    log::info!("Starting readlaterbot...");
    use teloxide::{adaptors::throttle::Limits, requests::RequesterExt, types::ParseMode};
    let bot = teloxide::Bot::new(std::env::var("TELEGRAM_BOT_TOKEN").unwrap())
        .parse_mode(ParseMode::MarkdownV2)
        .throttle(Limits::default()); //.unwrap();
    let handler = dptree::entry()
        //.branch(Update::filter_message().filter_command::<Command>().endpoint(command_answer))
        .branch(Update::filter_message().endpoint(messages_handler))
        .branch(Update::filter_callback_query().endpoint(handle_callback_query));
    Dispatcher::builder(bot, handler)
        .dependencies(dptree::deps![db.clone()])
        .default_handler(|upd| async move {
            log::warn!("Unhandled update: {:?}", upd);
        })
        // If the dispatcher fails for some reason, execute this handler.
        /*        .error_handler(LoggingErrorHandler::with_custom_text(
            "An error has occurred in the dispatcher",
        ))*/
        .enable_ctrlc_handler()
        .distribution_function(|_| None::<std::convert::Infallible>)
        .build()
        .dispatch()
        .await;
}
